package com.google.code.jerseyclients;

/*
 * Olivier Lamy
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.code.jerseyclients.asynchttpclient.AsyncHttpClientJerseyClient;
import com.google.code.jerseyclients.httpclientfour.ApacheHttpClientFour;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;

/**
 * @author <a href="mailto:olamy@apache.org">Olivier Lamy</a>
 * @since 1.0
 * @version $Id: JerseyClientBuilder.java 15 2011-02-28 15:35:12Z oliver.lamy $
 */
public class JerseyClientBuilder
{

    private Logger log = LoggerFactory.getLogger( getClass() );

    public static final short HTTP_CLIENT_4 = 2;

    public static final short JERSEY_DEFAULT = 3;

    public static final short ASYNC_HTTP_CLIENT = 4;

    private Map<String, Client> clientPerPath = new ConcurrentHashMap<String, Client>();

    private Map<Integer, Object> managerPerProvider = new ConcurrentHashMap<Integer, Object>();

    public void initManagerPerProvider( short impl, JerseyHttpClientConfig config )
    {
        switch ( impl )
        {
            case HTTP_CLIENT_4:

                SchemeRegistry schemeRegistry = new SchemeRegistry();

                schemeRegistry.register( new Scheme( "https", 443, SSLSocketFactory.getSocketFactory() ) );
                // http scheme
                schemeRegistry.register( new Scheme( "http", 80, PlainSocketFactory.getSocketFactory() ) );

                ThreadSafeClientConnManager threadSafeClientConnManager = new ThreadSafeClientConnManager(
                                                                                                           schemeRegistry );

                threadSafeClientConnManager.setMaxTotal( Integer.valueOf( config == null ? 0 : config
                    .getMaxConnections() ) );
                threadSafeClientConnManager.setDefaultMaxPerRoute( config == null ? 50 : config
                    .getMaxConnectionsPerHost() );

                managerPerProvider.put( Integer.valueOf( HTTP_CLIENT_4 ), threadSafeClientConnManager );
                break;
            case ASYNC_HTTP_CLIENT:
                //managerPerProvider.put( Integer.valueOf( ASYNC_HTTP_CLIENT ),
                //                        AyncHttpClientJerseyClient.create( config ) );
                break;
            default:
        }
    }

    public Object getManagerPerProvider( short impl )
    {
        return managerPerProvider.get( Integer.valueOf( impl ) );
    }

    /**
     * <b>not synchronized do it outside</b>
     * 
     * @param path if <code>null</code> remplaced by ""
     * @param config
     * @param impl see constants
     * @return
     */
    public Client build( String path, JerseyHttpClientConfig config, short impl )
    {
        log.debug( "get client for path {} with impl {}", new Object[] { path, Short.valueOf( impl ) } );
        Client client = clientPerPath.get( path == null ? "" : path );

        if ( client == null )
        {
            switch ( impl )
            {
                case JERSEY_DEFAULT:
                    client = Client.create( transformConfig(config));
                    break;
                case ASYNC_HTTP_CLIENT:
                    client = AsyncHttpClientJerseyClient.create( config );
                    break;
                case HTTP_CLIENT_4:
                default:
                    ThreadSafeClientConnManager threadSafeClientConnManager = (ThreadSafeClientConnManager) managerPerProvider
                        .get( Integer.valueOf( HTTP_CLIENT_4 ) );
                    client = ApacheHttpClientFour.create( config, threadSafeClientConnManager );
            }
            clientPerPath.put( path == null ? "" : path, client );
        }
        return client;
    }

    private ClientConfig transformConfig( JerseyHttpClientConfig config )
    {
        ClientConfig clientConfig = new DefaultClientConfig();
        clientConfig.getProperties().put( ClientConfig.PROPERTY_CONNECT_TIMEOUT, config.getConnectionTimeOut() );
        clientConfig.getProperties().put( ClientConfig.PROPERTY_READ_TIMEOUT, config.getReadTimeOut() );
        return clientConfig;
    }

    public synchronized void cleanupClientPerPath()
    {
        this.clientPerPath.clear();
    }

}
